﻿using System;
using System.Diagnostics.Contracts;
using System.IO;
using System.Windows.Media;
using OpenLS.Drawing;

namespace OpenLS.Spreadsheet.Formats.Biff.Records
{
    struct FullColorExtDouble
    {
        internal double _nTintShade;
        internal XColorType _xclrType;
        internal uint _xclrValue;

        internal const double TintMultiplier = 32768d;
        public FullColorExtDouble(SpreadsheetColor color)
        {
            if (color is ThemeColor)
            {
                _xclrType = XColorType.XCLRTHEMED;
                ThemeColor themeColor = (ThemeColor)color;
                this._xclrValue = (uint)themeColor.ColorIndex;
                this._nTintShade = themeColor.Tint.GetValueOrDefault(0) * TintMultiplier;
            }
            else if (color is AutomaticColor)
            {
                _xclrType = XColorType.XCLRAUTO;
                throw new NotImplementedException();
            }
            else if (color is IndexedColor)
            {
                _xclrType = XColorType.XCLRINDEXED;
                IndexedColor indexedColor = (IndexedColor) color;
                this._xclrValue = (uint) indexedColor.Index;
                this._nTintShade = 0;
            }
            else if (color is RgbColor)
            {
                _xclrType = XColorType.XCLRRGB;
                
                RgbColor rgbColor = (RgbColor) color;
                var c = rgbColor.Color;
                this._xclrValue = (uint) (c.R | (c.G << 8) | (c.B << 16) | (c.A << 24));
                this._nTintShade = 0;
            }
            else
            {
                throw new NotSupportedException();
            }
            Contract.Assert(ToColor().Equals(color));
        }

        public SpreadsheetColor ToColor()
        {
            switch (_xclrType)
            {
                case XColorType.XCLRRGB:
                    var r = (byte)((_xclrValue >> 0) & 0xFF);
                    var g = (byte)((byte)(_xclrValue >> 8) & 0xFF);
                    var b = (byte)((byte)(_xclrValue >> 16) & 0xFF);
                    var a = (byte)((byte)(_xclrValue >> 24) & 0xFF);
                    return new RgbColor(Color.FromArgb(a, r, g, b));
                case XColorType.XCLRTHEMED:
                    var tint = Convert.ToDouble(_nTintShade) / TintMultiplier;
                    return new ThemeColor((ColorSchemeIndex)_xclrValue, false ? null : (double?)(tint == 0? (double?)null: tint));
                case XColorType.XCLRAUTO:
                    return new AutomaticColor(false); //\\
                case XColorType.XCLRINDEXED:
                case XColorType.XCLRNINCHED:
                    throw new NotImplementedException(_xclrType.ToString());
                default:
                    throw new NotSupportedException();
            }

        }
    }
    struct FullColorExtShort 
    {
        internal short _nTintShade;
        internal XColorType _xclrType;
        internal uint _xclrValue;

       /* public FullColorExtShort(XColorType type, uint value, short tintShade)
        {
            this._xclrType = type;
            this._xclrValue = value;
            this._nTintShade = tintShade;
        }*/

        public FullColorExtShort(SpreadsheetColor color)
        {
            Contract.Requires(color != null);
            if (color is ThemeColor)
            {
                _xclrType = XColorType.XCLRTHEMED;
                ThemeColor themeColor = (ThemeColor)color;
                this._xclrValue = (uint)themeColor.ColorIndex;
                this._nTintShade = (short) (themeColor.Tint.GetValueOrDefault() * FullColorExtDouble.TintMultiplier);
            }
            else if (color is AutomaticColor)
            {
                _xclrType = XColorType.XCLRAUTO;
                this._nTintShade = 0;
                this._xclrValue = 0;
            }
            else if (color is IndexedColor)
            {
                _xclrType = XColorType.XCLRINDEXED;
                IndexedColor indexedColor = (IndexedColor) color;
                this._xclrValue = (uint) indexedColor.Index;
                this._nTintShade = 0;
            }
            else if (color is RgbColor)
            {
                _xclrType = XColorType.XCLRRGB;
                var rgbColor = ((RgbColor) color).Color;
                this._nTintShade = 0;
                this._xclrValue = (uint)((rgbColor.A << 24) | (rgbColor.B << 16) | (rgbColor.G << 8) | (rgbColor.R));
            }
            else
            {
                throw new NotSupportedException();
            }
            Contract.Assert(color.Equals(ToColor()) || color is ThemeColor);
        }

        public SpreadsheetColor ToColor()
        {
            switch (_xclrType)
            {
                case XColorType.XCLRRGB:
                    var a = (byte)((_xclrValue >> 24) & 0xFF);
                    var b = (byte)((byte)(_xclrValue >> 16) & 0xFF);
                    var g = (byte)((byte)(_xclrValue >> 8) & 0xFF);
                    var r = (byte)((byte)(_xclrValue >> 0) & 0xFF);
                    return new RgbColor(Color.FromArgb(a, r, g, b));
                case XColorType.XCLRTHEMED:
                    return new ThemeColor((ColorSchemeIndex)_xclrValue, false ? null : (double?)Convert.ToDouble(_nTintShade) / FullColorExtDouble.TintMultiplier);
                case XColorType.XCLRAUTO:
                    return new AutomaticColor(false);
                case XColorType.XCLRINDEXED:
                case XColorType.XCLRNINCHED:
                    throw new NotImplementedException(_xclrType.ToString());
                default:
                    throw new NotSupportedException();
            }

        }
    }
    internal class FullColorExt : ExtPropValueBase
    {
        private FullColorExtShort _value;
        private byte[] _unused = new byte[8];

        /*private FullColorExt(XColorType type, uint value, short tintShade)
        {
            _value = new FullColorExtShort(type, value, tintShade);
            _ToColor = ToColor();
        }*/
        private FullColorExt(SpreadsheetColor color)
        {
            _value = new FullColorExtShort(color);
#if DEBUG
            _ToColor = ToColor();
#endif
        }

        public FullColorExt()
        {
        }

#if DEBUG
        private SpreadsheetColor _ToColor;
#endif
        #region IExtPropValue Members

        public override void Read(BinaryReader reader)
        {
#if DEBUG

            ReadWriteChecker.CheckClass((t, r) => t.Read(r), (t, w) => t.Write(w), reader, this);
#endif
            this._value._xclrType = (XColorType) reader.ReadUInt16();
            this._value._nTintShade = reader.ReadInt16();
            this._value._xclrValue = reader.ReadUInt32();
            _unused = reader.ReadBytes(8);
#if DEBUG
            _ToColor = ToColor();

#endif
        }

        public override int Length
        {
            get { return 16; }
        }

        public override void Write(BinaryWriter writer)
        {
            writer.Write((ushort) _value._xclrType);
            writer.Write(_value._nTintShade);
            writer.Write(_value._xclrValue);
            writer.Write(_unused);
        }

        #endregion

        public SpreadsheetColor ToColor()
        {
            return _value.ToColor();
        }

        public static bool CanCreate (SpreadsheetColor color)
        {
            return color is ThemeColor || color is AutomaticColor || color is RgbColor;
        }
        public static FullColorExt Create(SpreadsheetColor color)
        {
            Contract.Requires(CanCreate(color));
            Contract.Ensures(Contract.Result<FullColorExt>() != null);
            Contract.Ensures(Contract.Result<FullColorExt>().ToColor().Equals(color) || (color is ThemeColor));
            return new FullColorExt(color);
            /*
            if (color is RgbColor)
            {
                Color rgbColor = ((RgbColor) color).Color;
                var value = (uint) ((rgbColor.A << 24) | (rgbColor.B << 16) | (rgbColor.G << 8) | (rgbColor.R));
               // var result = new FullColorExt(XColorType.XCLRRGB, value, 0);
//                result._xclrType = XColorType.XCLRRGB;

                //              result._xclrValue = (uint) ((rgbColor.R << 24) | (rgbColor.G << 16) | (rgbColor.B << 8) | (rgbColor.A));
                return result;
            }
            if (color is IndexedColor)
            {
                throw new NotSupportedException();
            }
            if (color is ThemeColor)
            {
                ThemeColor themeColor = (ThemeColor) color;
                var result = new FullColorExt(XColorType.XCLRTHEMED, (uint) themeColor.ColorIndex, (short) themeColor.Tint.GetValueOrDefault(0));
                return result;
            }
            if (color is AutomaticColor)
            {
                return new FullColorExt(XColorType.XCLRAUTO, 0, 0);
            }
            throw new NotSupportedException();
             * */
        }
    }
}